/**
 * Created by cy on 2021/08/05
 * @description 背景
 */
import Loading from "@/components/loading";
import { useEffect, useState } from "react";
import { TweenLite, Circ } from "gsap/all";
import { throttle } from "@/utils";

function Background(props) {
    let points = []; // 背景粒子
    let width = window.innerWidth;
    let height = window.innerHeight;
    let canvas = null; 
    let ctx = null;
    let target = {}; // 当前鼠标在屏幕的位置
    let myReq = null;

    /**
     * 异步加载背景图
     * @param {*} url 背景图片
     * @returns promise
     */
    const loadImgAsync = (url) => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = function () {
                resolve(url);
            };
            img.onerror = function () {
                console.log('图片载入错误')
            };
            img.src = url;
        })
    }

    /**
     * 创建背景粒子
     */
    const createPoints = () => {
        //创建粒子和粒子的起始位置
        for (let x = 0; x < width; x = x + width / 20) {
            for (let y = 0; y < height; y = y + height / 20) {
                let px = x + Math.random() * width / 20;
                let py = y + Math.random() * height / 20;
                let p = { x: px, originX: px, y: py, originY: py };
                points.push(p);
            }
        }
        //给每个粒子添加新属性closest、radius
        for (let i = 0; i < points.length; i++) {
            let closest = [];
            let p1 = points[i];
            for (let j = i + 1; j < points.length; j++) {
                let p2 = points[j];
                let placed = false;
                for (let k = 0; k < 5; k++) {
                    if (!placed) {
                        if (closest[k] === undefined) {
                            closest[k] = p2;
                            placed = true;
                        }
                    }
                }
                for (let k = 0; k < 5; k++) {
                    if (!placed) {
                        if (getDistance(p1, p2) < getDistance(p1, closest[k])) {
                            closest[k] = p2;
                            placed = true;
                        }
                    }
                }
            }
            p1.closest = closest;
            p1.radius = 2 + Math.random() * 2;
            //给粒子添加抖动
            shakePoint(p1);
        }
    }

    /**
     * 粒子抖动
     * @param {object} point 
     */
    const shakePoint = (point) => {
        TweenLite.to(point, 1 + 1 * Math.random(), {
            x: point.originX - 50 + Math.random() * 100,
            y: point.originY - 50 + Math.random() * 100, 
            ease: Circ.easeInOut,
            onComplete: () => {
                shakePoint(point);
            }
        });
    }
    /**
     * 绘制单个粒子
     * @param {*} point 
     * @param {*} ctx 
     */
    const drawPoint = (point, ctx) => {
        if (!point.pointActive) return;
        ctx.beginPath();
        ctx.arc(point.x, point.y, point.radius, 0, 2 * Math.PI, false);
        ctx.fillStyle = 'rgba(156,217,249,' + point.pointActive + ')';
        ctx.fill();
    }
    /**
     * 绘制线条
     */
    const drawLines = (point, ctx) => {
        if (!point.lineActive) return;
        for (let item of point.closest) {
            ctx.beginPath();
            ctx.moveTo(point.x, point.y);
            ctx.lineTo(item.x, item.y);
            ctx.strokeStyle = 'rgba(156,217,249,' + point.lineActive + ')';
            ctx.stroke();
        }
    }
    /**
     * 获取两个粒子之间的距离
     * @param {object} p1 
     * @param {object} p2 
     */
    const getDistance = (p1, p2) => {
        return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
    }
    /**
    * 鼠标移动事件处理
    * @param {*} e 
    */
    const handleMouseMove = (e) => {
        let posx = 0, posy = 0;
        if (e.pageX || e.pageY) {
            posx = e.pageX;
            posy = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
            posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
        }
        target.x = posx;
        target.y = posy;
    };
    /**
     * 开始函数
     */
    const start = () => {
        ctx.clearRect(0, 0, width, height);
        for (let point of points) {
            if (Math.abs(getDistance(target, point)) < 4000) {
                point.lineActive = 0.3;
                point.pointActive = 0.6;
            } else if (Math.abs(getDistance(target, point)) < 20000) {
                point.lineActive = 0.1;
                point.pointActive = 0.3;
            } else if (Math.abs(getDistance(target, point)) < 40000) {
                point.lineActive = 0.02;
                point.pointActive = 0.1;
            } else {
                point.lineActive = 0;
                point.pointActive = 0;
            }
            drawLines(point, ctx);
            drawPoint(point, ctx);
        }
        myReq = window.requestAnimationFrame(() => start());
    };

    const init = () => {
        ctx = canvas.getContext('2d');
        createPoints();
        start();
        myReq = window.onmousemove = throttle(handleMouseMove, 50)   //函数节流优化
    }
    const { url, children } = props;
    const [loading, setLoading] = useState(false);

    useEffect(() => {
        setLoading(true);
        loadImgAsync(url).then(() => {
            setLoading(false)
        }).then(() => {
            canvas && init()
        })
        return () => {
            window.cancelAnimationFrame(myReq);
            window.onmousemove = null
        }
    }, [])

    return (
        <div>
            {
                loading? (
                    <div style={styles.backgroundBox}>
                        <Loading />
                    </div>
                ) : (
                    <div style={{...styles.backgroundBox, backgroundImage: `url(${url})`}}>
                        {/* <canvas
                            ref={el => this.canvas = el}
                            style={styles.canvasStyle}
                            width={this.width}
                            height={this.height} /> */}
                        {children}
                    </div>
                )
            }
        </div>
    )
}

const styles = {
    backgroundBox: {
        position: 'fixed',
        top: '0',
        left: '0',
        width: '100vw',
        height: '100vh',
        backgroundSize: '100% 100%',
    },
    canvasStyle: {
        display: 'block',   //防止全屏的canvas出现滚动条
        position: 'fixed',
        top: '0',
        left: '0',
    }
};

export default Background;
